home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Compression / Opener / Source / Process.m < prev    next >
Text File  |  1993-07-19  |  7KB  |  273 lines

  1. /*
  2.  * Process -- manage i/o with simple subprocesses.
  3.  * Considerably tweaked relative of /NextDeveloper/Examples/Subprocess.
  4.  * M. J. Hawley
  5.  * Copyright (c) MIT Media Laboratory
  6.  * mike@media-lab.mit.edu
  7.  */
  8. #import "Process.h"
  9.  
  10. @interface Process(Private)
  11. - childDidExit;
  12. - fdHandler:(int)theFd;
  13. @end
  14.  
  15. static void
  16. showError (const char *s, id delegate){ // ensure errors never get lost
  17.     if (delegate && [delegate respondsTo:@selector(processError:)])
  18.     [delegate perform:@selector(processError:) with:(void *)s];
  19.     else if (NXApp)    // no delegate, but we're running w/in an App
  20.     NXRunAlertPanel(0, s, 0, 0, 0);
  21.     else
  22.     perror(s);
  23. }
  24.  
  25. static void
  26. fdHandler (int fd, id self) { // DPS handler for output from process
  27.     [self fdHandler:fd];
  28. }
  29.  
  30. static void
  31. getptys(int *master, int *slave){ // attempt to setup the ptys
  32.     #define    PTY_TEMPLATE "/dev/pty??"
  33.     #define    PTY_LENGTH 11
  34.     char device[PTY_LENGTH];
  35.     char *block, *num;
  36.     char *blockLoc; // specifies the location of block for the device string
  37.     char *numLoc; // specifies the pty name with the digit ptyxD
  38.     char *msLoc; // specifies the master (ptyxx) or slave (ttyxx)
  39.     
  40.     struct sgttyb setp = {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
  41.     struct tchars setc = {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
  42.     struct ltchars sltc = {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
  43.     int    lset = (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
  44.     int    setd = NTTYDISC;
  45.     
  46.     strcpy(device, PTY_TEMPLATE); // string constants are not writable
  47.     blockLoc = &device[ strlen("/dev/pty") ];
  48.     numLoc = &device[ strlen("/dev/pty?") ];
  49.     msLoc = &device[ strlen("/dev/") ];
  50.     for (block = "pqrs"; *block; block++){
  51.     *blockLoc = *block;
  52.     for (num = "0123456789abcdef"; *num; num++) {
  53.         *numLoc = *num;
  54.         *master = open(device, O_RDWR);
  55.         if (*master >= 0) {
  56.         *msLoc = 't';
  57.         *slave = open(device, O_RDWR);
  58.         if (*slave >= 0) {
  59.             (void) ioctl(*slave, TIOCSETP, (char *)&setp);
  60.             (void) ioctl(*slave, TIOCSETC, (char *)&setc);
  61.             (void) ioctl(*slave, TIOCSETD, (char *)&setd);
  62.             (void) ioctl(*slave, TIOCSLTC, (char *)&sltc);
  63.             (void) ioctl(*slave, TIOCLSET, (char *)&lset);
  64.             return;
  65.         }
  66.         }
  67.     } /* hunting through a bank of ptys */
  68.     } /* hunting through blocks of ptys in all the right places */
  69.     *master = -1;
  70.     *slave = -1;
  71. }
  72.  
  73. static int
  74. iwait(fd, timeout)
  75.         long fd;
  76.         unsigned long timeout;  /* in seconds */
  77. /*
  78.  * Wait until 'fd' is ready for reading, or 'timeout'.
  79.  * Return '>=0' when 'fd' is readable, '0' if timeout, '-1' on error.
  80.  * Example: 'iwait(f,0)' polls a file descriptor
  81.  * without blocking and returns true if it's readable;
  82.  * e.g., 'iwait(0,0)' is true when standard input is contains something.
  83.  */
  84. {
  85.         struct timeval t;
  86.         int readfd = 1<<fd;
  87.  
  88.         t.tv_sec = timeout, t.tv_usec = 0;
  89.         return (int)select(sizeof(int)*8, (fd_set *)&readfd, (fd_set *)0, (fd_set *)0, &t);
  90. }
  91.  
  92. @implementation Process(Private)
  93.  
  94. - childDidExit { // cleanup after a child process exits
  95.     if (childPid) {
  96.     if (from) DPSRemoveFD(from);
  97.     close(from);
  98.     if (fpTo) fclose(fpTo);
  99.     if (fpFrom) fclose(fpFrom);
  100.     fpTo = fpFrom = (FILE *)0;
  101.     childPid=0;    // specify that child is dead
  102.     if (delegate && [delegate respondsTo:@selector(processDone)])
  103.         [delegate perform:@selector(processDone)];
  104.     }
  105.     return self;
  106. }
  107.  
  108. - fdHandler:(int)fd { // DPS handler for output from process
  109.     if (((bufferCount=read(fd,buffer,BUFSIZE-1))<0)||(!bufferCount))
  110.     return [self childDidExit];
  111.     buffer[bufferCount] = '\0';
  112.     if (delegate && [delegate respondsTo:action])
  113.     [delegate perform:action with:(void *)&buffer];
  114.     return self;
  115. }
  116.  
  117. @end
  118.  
  119.  
  120. @implementation Process
  121.  
  122. + new:(char *)process delegate:del {
  123.     self = [Process alloc];
  124.     [self init:process delegate:del andPty:YES andStderr:YES];
  125.     return self;
  126. }
  127.  
  128. + new:(char *)process delegate:del andPty:(BOOL)wantsPty andStderr:(BOOL)wantsStderr {
  129.     self = [Process alloc];
  130.     [self init:process delegate:del andPty:wantsPty andStderr:wantsStderr];
  131.     return self;
  132. }
  133.  
  134. - init:(char *)process delegate:del {
  135.     return [self init:process delegate:del andPty:NO andStderr:NO];
  136. }
  137.  
  138. - init:(char *)process delegate:del andPty:(BOOL)pty andStderr:(BOOL)err {
  139.     // initializes an instance of process and corresponding UNIX process
  140.     int pipeTo[2];
  141.     int pipeFrom[2];
  142.     int    tty, numFds, fd;    // for temporary use
  143.     int processGroup;
  144.     int pidChild;        // needed because childPid does not exist
  145.                 // until process is instantiated
  146.  
  147.     [self setAction:@selector(processOutput:)];
  148.     if (pty){
  149.         tty = open("/dev/tty", O_RDWR);
  150.     getptys(&masterPty,&slavePty);
  151.     if (masterPty <= 0 || slavePty <= 0) {
  152.         showError("Error grabbing ptys for subprocess.", del);
  153.         return self;
  154.     }
  155.     // remove the controlling tty if launched from a shell,
  156.     // but not Workspace;
  157.     // so that we have job control over the parent application in shell
  158.     // and so that subprocesses can be restarted in Workspace
  159.     if  ((tty<0) && ((tty = open("/dev/tty", 2))>=0)) {
  160.         ioctl(tty, TIOCNOTTY, 0);
  161.         close(tty);
  162.     }
  163.     } else
  164.     if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0){
  165.     showError("Error starting UNIX pipes to process.", del);
  166.     return self;
  167.     }
  168.     
  169.     switch (pidChild = vfork()){
  170.     case -1:    // error
  171.     showError("Error starting UNIX vfork of process.", del);
  172.     return self;
  173.  
  174.     case 0:    // child
  175.     if (pty) {
  176.         dup2(slavePty, 0);
  177.         dup2(slavePty, 1);
  178.         if (err) dup2(slavePty, 2);
  179.     } else {
  180.         dup2(pipeTo[0], 0);
  181.         dup2(pipeFrom[1], 1);
  182.         if (err) dup2(pipeFrom[1], 2);
  183.     }
  184.     
  185.     numFds = getdtablesize();
  186.     for (fd=3; fd<numFds; fd++)
  187.         close(fd);
  188.  
  189.     processGroup = getpid();
  190.     ioctl(0, TIOCSPGRP, (char *)&processGroup);
  191.     setpgrp (0, processGroup);
  192.     
  193.     // execl(process, 0);
  194.     execl("/bin/sh", "sh", "-c", process, 0);
  195.     perror("vfork (child)"); // should never gets here tho
  196.     exit(1);
  197.  
  198.     default:    // parent
  199.     [self setDelegate:del];
  200.     childPid = pidChild;
  201.     if (pty){
  202.         close(slavePty);
  203.         fpTo = fdopen(masterPty, "w");
  204.         from = masterPty;
  205.         fpFrom = fdopen(masterPty, "r");
  206.     } else {
  207.         close(pipeTo[0]);
  208.         close(pipeFrom[1]);
  209.     
  210.         fpTo = fdopen(pipeTo[1], "w");
  211.         from = pipeFrom[0];
  212.         fpFrom = fdopen(pipeFrom[0], "r");
  213.         }
  214.     setbuf(fpTo, NULL);
  215.     setbuf(fpFrom, NULL);
  216.     DPSAddFD(from,(DPSFDProc)fdHandler,(id)self,NX_MODALRESPTHRESHOLD+1);
  217.     // printf("added %d, %d, %d [%d %d]\n",from, self, delegate, pipeTo[1], pipeFrom[0]);
  218.     return self;
  219.     }
  220. }
  221.  
  222. - puts:(char *)s {
  223.     fputs(s, fpTo);
  224.     return self;
  225. }
  226.  
  227.  
  228. static void
  229. _fgets(s,n,f) char *s; int n; FILE *f; {
  230.     int fd = fileno(f);
  231.     while (iwait(fd,3)>0){
  232.         if (read(fd,s,1) != 1){
  233.             *s = '\0';
  234.             return;
  235.         }
  236.         if (*s=='\n' || --n <= 0){
  237.             *++s = '\0';
  238.             return ;
  239.         }
  240.         if (*s != '\r') ++s;
  241.     }
  242.     *++s = '\0';
  243. }
  244.  
  245. - gets:(char *)s :(int)n {
  246.     *s = '\0';
  247.     _fgets(s,n,fpFrom);
  248.     return self;
  249. }
  250.  
  251. - terminate:sender{
  252.     if (childPid){
  253.     kill(childPid+1, SIGTERM);
  254.     [self childDidExit];
  255.     }
  256.     return self;
  257. }
  258.  
  259. - setDelegate:anObject {
  260.     delegate = anObject;
  261.     return self;
  262. }
  263.  
  264. - delegate {
  265.     return delegate;
  266. }
  267.  
  268. - setAction:(SEL)theAction {
  269.     action = theAction;
  270.     return self;
  271. }
  272. @end
  273.